#include "test_log.h"
#include "../defs.h"
#include "../virtio_disk.h"
#include "../param.h"
#include <assert.h>



//TODO: module structure will move to assert_ops;
static void assert_buf_all_eq_char(struct buf * b, uchar c){
  for(int i = 0; i < BSIZE; ++i){
    assert(b->data[i] ==  c);
  }
}
static void assert_mem_block_all_char(uint dev, uint blockno, uchar c) {
  assert(1 == t__block_in_bcache(dev, blockno));

  struct buf * b = bread(dev, blockno);
  assert_buf_all_eq_char(b, c);
  brelse(b);
}
static void assert_disk_block_all_eq_char(uint blockno, uchar c) {
  struct buf n;
  buf_init_zero_data(&n, blockno);

  virtio_disk_read(&n);
  assert_buf_all_eq_char(&n, c);
}


static void __format_log(){
  virtio_disk_zero__();
  initlog(ROOTDEV, 2, LOGSIZE); 
  assert(0 == t__log_num());
}
static void __bwrite_disk(uint blockno, uchar c) {
  struct buf * b = bread(ROOTDEV, blockno);
  buf_set_char(b, c);
  assert_buf_all_eq_char(b, c);

  log_write(b);
  brelse(b); 
}







static void test_write_disk() {
  __format_log();

  begin_op();

  __bwrite_disk(2 + LOGSIZE, '4');
  assert(1 == t__log_num());
  assert_mem_block_all_char(ROOTDEV, 2 + LOGSIZE, '4');
  
  assert_disk_block_all_eq_char(2 + 1, '\0'); //OK: no disk operation in the log[0]-block
  assert_disk_block_all_eq_char(2 + LOGSIZE, '\0');//OK: dest data[0]-block has no disk operation

  end_op();
  assert(0 == t__log_num());

  assert_disk_block_all_eq_char(2 + 1, '4');
  assert_disk_block_all_eq_char(2 + LOGSIZE, '4');
}

static void test_multi_write_disk() {
  __format_log();

  begin_op();

  __bwrite_disk(2 + LOGSIZE, '4');
  assert(1 == t__log_num());
  assert_disk_block_all_eq_char(2 + 1, '\0');

  __bwrite_disk(2 + LOGSIZE + 1, '5');
  assert(2 == t__log_num());
  assert_disk_block_all_eq_char(2 + 2, '\0');

  __bwrite_disk(2 + LOGSIZE + 2, '6');
  assert(3 == t__log_num());
  assert_disk_block_all_eq_char(2 + 3, '\0');

  end_op();
  assert(0 == t__log_num());

  assert_disk_block_all_eq_char(2 + 1, '4');
  assert_disk_block_all_eq_char(2 + LOGSIZE, '4');

  assert_disk_block_all_eq_char(2 + 2, '5');
  assert_disk_block_all_eq_char(2 + LOGSIZE + 1, '5');

  assert_disk_block_all_eq_char(2 + 3, '6');
  assert_disk_block_all_eq_char(2 + LOGSIZE + 2, '6');
}

static void test_multi_begin() {
  __format_log();

  begin_op();
  begin_op();

  __bwrite_disk(2 + LOGSIZE, '4');
  assert_disk_block_all_eq_char(2 + 1, '\0');

  end_op();
  assert_disk_block_all_eq_char(2 + 1, '\0');

  begin_op();

  __bwrite_disk(2 + LOGSIZE + 1, '5');
  assert_disk_block_all_eq_char(2 + 1, '\0');
  assert_disk_block_all_eq_char(2 + 2, '\0');

  end_op();
  assert_disk_block_all_eq_char(2 + 1, '\0');
  assert_disk_block_all_eq_char(2 + 2, '\0');

  end_op();
  assert_disk_block_all_eq_char(2 + 1, '4');
  assert_disk_block_all_eq_char(2 + LOGSIZE, '4');
  assert_disk_block_all_eq_char(2 + 2, '5');
  assert_disk_block_all_eq_char(2 + LOGSIZE + 1, '5');
}

static void test_multi_end() {
  __format_log();

  begin_op();

  __bwrite_disk(2 + LOGSIZE, '4');

  assert_disk_block_all_eq_char(2 + 1, '\0');
  assert_disk_block_all_eq_char(2 + LOGSIZE, '\0');

  end_op();
  assert_disk_block_all_eq_char(2 + 1, '4');
  assert_disk_block_all_eq_char(2 + LOGSIZE, '4');

  end_op();
  assert(-1 == t__log_outstanding());

  begin_op();
  assert(0 == t__log_outstanding());

  __bwrite_disk(2 + LOGSIZE + 1, '5');
  assert_disk_block_all_eq_char(2 + 2, '\0');

  end_op();
  assert_disk_block_all_eq_char(2 + 2, '5');
  assert_disk_block_all_eq_char(2 + LOGSIZE + 1, '5');
}

static void test_lack_begin() {
  __format_log();

  __bwrite_disk(2 + LOGSIZE, '4');
  end_op();
}

static void test_over_log_size() {
  __format_log();

  begin_op();

  for(int i = 0; i <= LOGSIZE; ++i){
    __bwrite_disk(2 + LOGSIZE + i, '4');
  }

  end_op();
}

void test_log(){
  test_write_disk();
  test_multi_write_disk();
  test_multi_begin();
  //test_multi_end();
  //test_lack_begin(); //panic
  //test_over_log_size(); //panic

}

